﻿using System;
using UnityEngine;

namespace DL.UIBinding
{   
    /// <summary>
    /// 通知属性更新的委托
    /// </summary>
    public delegate void NotifyPropertyChanged(bool forceChange);

    public abstract class Property
    {
        /// <summary>
        /// 本属性的改变事件
        /// </summary>
        public event NotifyPropertyChanged OnChange;

        //在ValueChange中调用，只有属性值修改了才会调用
        protected void ValueChanged(bool forceChange)
        {
            if (OnChange != null) OnChange.Invoke(forceChange);
        }
    }

    public class Property<T> : Property
    {
        private readonly bool _isValue; //是否是值类型
        /// <summary>
        /// 正在修改
        /// </summary>
        private bool _changing; 
        //存储值的变量
        protected T Value;

        public Property()
        {
            _isValue = typeof(T).IsValueType;
        }

        public Property(T value) : this()
        {
            Value = value;
        }

        public bool IsOfType(Type t)
        {
            return t == typeof(T);
        }

        public T GetValue()
        {
            return Value;
        }

        protected virtual bool IsValueDifferent(T value)
        {
            return !Value.Equals(value);
        }

        private bool IsClassDifferent(T value)
        {
            return !Value.Equals(value);
        }

        public virtual void SetValue(T value, bool forceChange = false)
        {
            //保证数据安全
            if (_changing && !forceChange) return;
            
            _changing = true;

            bool changed;

            if (_isValue)
                changed = IsValueDifferent(value);
                // changed = true;
            else
                //bool || 引用类型
                changed = (value == null && Value != null || value != null && Value == null ||
                           Value != null && IsClassDifferent(value));
            //如果不同
            if (changed)
            {
                //赋值操作
                Value = value;
                //调用OnChange事件  
                ValueChanged(forceChange);
            }

            _changing = false;
        }
    }

    public sealed class FloatProperty : Property<float>
    {
        public FloatProperty()
        {
        }

        public FloatProperty(float value)
        {
            Value = value;
        }

        protected override bool IsValueDifferent(float value)
        {
            return !Mathf.Approximately(GetValue(), value);
        }
    }

    public sealed class IntProperty : Property<int>
    {
        public IntProperty()
        {
        }

        public IntProperty(int value)
        {
            Value = value;
        }

        protected override bool IsValueDifferent(int value)
        {
            return GetValue() != value;
        }
    }

    public sealed class DoubleProperty : Property<double>
    {
        public DoubleProperty()
        {
        }

        public DoubleProperty(double value)
        {
            Value = value;
        }

        protected override bool IsValueDifferent(double value)
        {
            return !Mathf.Approximately((float) GetValue(), (float) value);
        }
    }

    public sealed class BoolProperty : Property<bool>
    {
        public BoolProperty()
        {
        }

        public BoolProperty(bool value)
        {
            Value = value;
        }

        protected override bool IsValueDifferent(bool value)
        {
            return GetValue() != value;
        }
    }


    public sealed class ColorProperty : Property<Color>
    {
        public ColorProperty()
        {
        }

        public ColorProperty(Color value)
        {
            Value = value;
        }

        protected override bool IsValueDifferent(Color value)
        {
            return GetValue() != value;
        }
    }

    public sealed class Vector2Property : Property<Vector2>
    {
        public Vector2Property()
        {
        }

        public Vector2Property(Vector2 value)
        {
            Value = value;
        }

        protected override bool IsValueDifferent(Vector2 value)
        {
            return GetValue() != value;
        }
    }

    public sealed class Vector3Property : Property<Vector3>
    {
        public Vector3Property()
        {
        }

        public Vector3Property(Vector3 value)
        {
            Value = value;
        }


        protected override bool IsValueDifferent(Vector3 value)
        {
            return GetValue() != value;
        }
    }

    public sealed class QuaternionProperty : Property<Quaternion>
    {
        public QuaternionProperty()
        {
        }


        public QuaternionProperty(Quaternion value)
        {
            Value = value;
        }

        protected override bool IsValueDifferent(Quaternion value)
        {
            return GetValue() != value;
        }
    }


    public sealed class Vector4Property : Property<Vector4>
    {
        public Vector4Property()
        {
        }

        public Vector4Property(Vector4 value)
        {
            Value = value;
        }


        protected override bool IsValueDifferent(Vector4 value)
        {
            return GetValue() != value;
        }
    }

    public sealed class ByteProperty : Property<byte>
    {
        public ByteProperty()
        {
        }

        public ByteProperty(byte value)
        {
            Value = value;
        }

        protected override bool IsValueDifferent(byte value)
        {
            return GetValue() != value;
        }
    }

    public sealed class LongProperty : Property<long>
    {
        public LongProperty()
        {
        }


        public LongProperty(long value)
        {
            Value = value;
        }

        protected override bool IsValueDifferent(long value)
        {
            return GetValue() != value;
        }
    }

    public sealed class ShortProperty : Property<short>
    {
        public ShortProperty()
        {
        }

        public ShortProperty(short value)
        {
            Value = value;
        }

        protected override bool IsValueDifferent(short value)
        {
            return GetValue() != value;
        }
    }
}